package com.mm.base.config.db;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Slf4jReporter;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import java.util.concurrent.TimeUnit;

/**
 * @Description HikariCP Config Monitoring
 * @Date 2024/11/22 上午10:38
 * @Author yanglin
 **/
@Slf4j
//@Configuration
public class HikariDataSourceConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    public HikariDataSource dataSource(DataSourceProperties properties) {
        HikariDataSource dataSource = properties.initializeDataSourceBuilder()
                .type(HikariDataSource.class)
                .build();
        if (StringUtils.hasText(properties.getName())) {
            dataSource.setPoolName(properties.getName());
        }
        dataSource.setMetricRegistry(initMetricRegistry(dataSource.getPoolName()));
        return dataSource;
    }

    /**
     * 指标记录，默认：none。这个属性只适用于程序配置或ioc容器，
     * 允许你指定一个Codahale/Dropwizard MetricRegistry来记录指标。
     *
     * Metrics一般支持五种类型：
     * Gauge：常规计量，用来统计瞬时状态的数据信息，如系统中处于pending状态的job。
     * Counter：Gauge的特例，维护一个计数器，用于累计值。
     * Meters：度量某个时间段的平均处理次数（request per second），如每1、5、15分钟的TPS。
     * Histograms：统计数据的分布情况，最大值、最小值、平均值、中位数，百分比（75%、90%、95%、98%、99%和99.9%）。
     * Timers：统计某段代码段执行时间及分布，是基于Histograms和Meters实现的。
     *
     * 输出指标说明: 打印指标的格式为{连接池名称}.pool.{指标}
     * ActiveConnections	    活跃连接数	此数据长期保持最大连接数值的时候可以尝试扩大连接数
     * IdleConnections	        空闲连接数	此数据过高的时候可以尝试减少配置中的最小连接数
     * MaxConnections	        配置的最大连接数
     * MinConnections	        配置的最小连接数
     * PendingConnections	    排队等待连接的线程数	如果此数据持续飙高，表示连接池中已经没有空闲线程了
     * TotalConnections	        当前总连接数
     * ConnectionCreation	    创建新连接的耗时	此数据主要反应当前服务到数据服务的网络延迟
     * ConnectionTimeoutRate	创建新连接的超时	如果经常创建连接超时这个时候需要排查数据服务或者网络通讯是否异常
     * Usage	                连接被复用时长	    此参数表示连接池中一个连接从返回连接池到再次被复用的时间间隔，表示数据访问频繁程度，对于使用较长的间隔可以尝试减少连接数
     * Wait	                    获取连接的等待耗时	可以和PendingConnections结合分析连接池情况。
     * Wait和PendingConnections结合分析连接池情况
     * 如果排队多等待短：此时表示数据访问频繁可以尝试扩大连接数；
     * 如果排队少等待长：此时连接中存在慢查询或者比较大的事务；
     * 如果排队多等待长：此时可能是数据访问压力过大且存在大量慢查询，但实际上如果频繁出现慢查询很有可能是程序或者业务上出现了问题，
     * 需要对业务和代码进行排查。这种时刻也能网络出现异常导致所有查询都变得非常慢；
     *
     * @param poolName
     * @return
     */
    public MetricRegistry initMetricRegistry(String poolName) {
        MetricRegistry metricRegistry = new MetricRegistry();
        Slf4jReporter reporter = Slf4jReporter.forRegistry(metricRegistry)
                .outputTo(log)
                .convertRatesTo(TimeUnit.SECONDS)
                .convertDurationsTo(TimeUnit.MILLISECONDS)
                .build();
        // 每30秒打印一次度量信息
        reporter.start(60, TimeUnit.SECONDS);
        return metricRegistry;
    }
}
